package com.start.workflow.core;

import cn.hutool.core.util.StrUtil;

import com.start.common.exceptions.StartBusException;
import com.start.common.std.StartBaseSingleton;
import com.start.common.std.StartSingletonFactory;
import com.start.common.tools.SpringContextTools;
import org.activiti.bpmn.constants.BpmnXMLConstants;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.VariableScope;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.activiti.engine.runtime.Execution;
import org.apache.commons.lang3.StringUtils;


import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 流程节点预测工具
 */
public class StartProcTaskTools extends StartBaseSingleton<StartProcTaskTools> {
    private static final List<String> elementTypes = new ArrayList<>();
    private static ProcessEngineConfigurationImpl processEngineConfiguration;

    static {
        elementTypes.add(BpmnXMLConstants.ELEMENT_TASK_USER);
        elementTypes.add(BpmnXMLConstants.ELEMENT_GATEWAY_EXCLUSIVE);
    }

    @Override
    public void init() {
        processEngineConfiguration = SpringContextTools.getBean(ProcessEngineConfigurationImpl.class);
    }

    public static StartProcTaskTools getInstance() {
        return StartSingletonFactory.get(StartProcTaskTools.class);
    }

    /**
     * 查找下一环节
     *
     * @param bpmnModel
     * @param procDefKey
     * @param currentTaskDefKey
     * @param execution
     * @return
     */
    public FlowElement nextFlowElement(BpmnModel bpmnModel, String procDefKey, String currentTaskDefKey, Execution execution) {
        ExecutionEntityImpl variableContainer = null;
//        if (execution == null) return null;
//        if (!(execution instanceof ExecutionEntityImpl)) {
//            return null;
//        }
//        variableContainer = (ExecutionEntityImpl) execution;
        return nextFlowElementVariableContainer(bpmnModel, procDefKey, currentTaskDefKey, null);
    }

    /**
     * 查找下一环节
     *
     * @param bpmnModel
     * @param procDefKey
     * @param currentTaskDefKey
     * @param variableContainer
     * @return
     */
    public FlowElement nextFlowElementVariableContainer(BpmnModel bpmnModel, String procDefKey, String currentTaskDefKey, VariableScope variableContainer) {
        Process process = StringUtils.isAllBlank(procDefKey) ? bpmnModel.getMainProcess() : bpmnModel.getProcessById(procDefKey);
        if (process == null) {
            process = bpmnModel.getProcesses().get(0);
        }
        FlowElement flowElement = process.getFlowElement(currentTaskDefKey);
        String simpleName = StrUtil.lowerFirst(flowElement.getClass().getSimpleName());
        if (!elementTypes.contains(simpleName)) {
            throw new StartBusException("当前环节不是一个有效的节点元素:" + currentTaskDefKey);
        }
        FlowNode curFlowNode = (FlowNode) flowElement;
        return nextFlowElementVariableContainer(process, curFlowNode, variableContainer, null);
    }


    public FlowElement nextFlowElementVariableContainer(Process process, FlowNode curFlowNode, VariableScope variableContainer, Set<String> sequenceFlowIds) {
        String simpleName = StrUtil.lowerFirst(curFlowNode.getClass().getSimpleName());
        if (!elementTypes.contains(simpleName)) {
            throw new StartBusException("当前环节不是一个有效的节点元素:" + curFlowNode);
        }
//        因暂时无法处理条件表达式 故暂时不做调整
//        if (sequenceFlowIds == null) {
//            sequenceFlowIds = new HashSet<>();
//        }
//
//        List<SequenceFlow> outgoingFlows = curFlowNode.getOutgoingFlows();
//        if (outgoingFlows == null || outgoingFlows.isEmpty()) {
//            return null;
//        }
//        FlowElement defaultFlowElement = getNextFlowElementDefaultUserTask(process, curFlowNode, new HashSet<>());
//        for (SequenceFlow sequenceFlow : outgoingFlows) {
//            if (sequenceFlowIds.contains(sequenceFlow.getId())) {
//                continue;
//            }
//            sequenceFlowIds.add(sequenceFlow.getId());
//            if (!hasTrueCondition(sequenceFlow, variableContainer)) {
//                continue;
//            }
//            FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
//            if (targetFlowElement instanceof UserTask) {
//                return targetFlowElement;
//            }
//            if (targetFlowElement instanceof ExclusiveGateway) {
//                FlowNode targetFlowNode = (FlowNode) targetFlowElement;
//                FlowElement flowElement = nextFlowElementVariableContainer(process, targetFlowNode, variableContainer, sequenceFlowIds);
//                if (flowElement != null) return flowElement;
//            }
//        }
        return getNextFlowElementDefaultUserTask(process, curFlowNode, new HashSet<>());
    }

    /**
     * 查找默认的用户连接点
     *
     * @param process         当前流程图
     * @param curFlowNode     当前节点
     * @param sequenceFlowIds 已查找流程线id
     * @return
     */
    private FlowElement getNextFlowElementDefaultUserTask(Process process, FlowNode curFlowNode, Set<String> sequenceFlowIds) {
        if (sequenceFlowIds == null) {
            sequenceFlowIds = new HashSet<>();
        }
        String defaultFlowKey = null;
        if (curFlowNode instanceof UserTask) {
            defaultFlowKey = ((UserTask) curFlowNode).getDefaultFlow();
        } else if (curFlowNode instanceof ExclusiveGateway) {
            defaultFlowKey = ((ExclusiveGateway) curFlowNode).getDefaultFlow();
        }
        if (StringUtils.isNotBlank(defaultFlowKey)) {
            FlowElement targetElement = process.getFlowElement(defaultFlowKey);
            if (targetElement instanceof UserTask) {
                return targetElement;
            }
            if (!(targetElement instanceof ExclusiveGateway)) {
                return targetElement;
            }
            return getNextFlowElementDefaultUserTask(process, (FlowNode) targetElement, sequenceFlowIds);
        }

        List<SequenceFlow> outgoingFlows = curFlowNode.getOutgoingFlows();
        if (outgoingFlows.size() > 2) {
            return null;
        }
        FlowElement targetElement = outgoingFlows.get(0).getTargetFlowElement();
        if (targetElement instanceof UserTask) {
            return targetElement;
        }
        if (!(targetElement instanceof ExclusiveGateway)) {
            return null;
        }
        return getNextFlowElementDefaultUserTask(process, (FlowNode) targetElement, sequenceFlowIds);
    }

    /**
     * 处理条件判断
     *
     * @param sequenceFlow      流程连线
     * @param variableContainer 流程变量容器
     * @return
     */
    public boolean hasTrueCondition(SequenceFlow sequenceFlow, VariableScope variableContainer) {
        String conditionExpression = sequenceFlow.getConditionExpression();
        if (StringUtils.isAllBlank(conditionExpression)) {
            return true;
        }
        ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager();
        Expression expression = expressionManager.createExpression(conditionExpression);
        Object result = expression.getValue(variableContainer);
//        if (result == null) {
//            throw new FlowableException("condition expression returns null (sequenceFlowId: " + sequenceFlow.getId() + ")");
//        }
//        if (!(result instanceof Boolean)) {
//            throw new FlowableException("condition expression returns non-Boolean (sequenceFlowId: " + sequenceFlow.getId() + "): " + result + " (" + result.getClass().getName() + ")");
//        }
        return (Boolean) result;
    }


}
